#include "../tlsf/TlsfStdString.h"
#include <cassert>
#include <stdexcept>
#include <cstring>
#include <algorithm>
#include <string>
#include <iostream>

#ifdef NDEBUG
#undef NDEBUG
#endif

void test_constructor() {
}

void test_at() {
    como::TlsfString s("abc");
    s.at(2) = 'x';
    assert(s == "abx");

    try {
        s.at(3) = 'x';
    } catch (std::out_of_range const& exc) {
        return;
    }
    assert(false);
}

void test_operator_at() {
    como::TlsfString const e("Exemplar");
    std::string result;
    for (auto i = e.length() - 1; i != 0; i /= 2)
        result += e[i];
    assert(result == "rmx");

    const char * c = &e[0];
    assert(std::string(c) == "Exemplar");

    como::TlsfString s("Exemplar ");
    s[s.size() - 1] = 'y';
    assert(s == "Exemplary");
}

void test_front() {
    como::TlsfString s("Exemplary");
    char& f1 = s.front();
    f1 = 'e';
    assert(s == "exemplary");
    como::TlsfString const c("Exemplary");
    char const & f2 = c.front();
    assert(std::string(&f2) == "Exemplary");
}

void test_back() {
    como::TlsfString s("Exemplary");
    char & back1 = s.back();
    back1 = 's';
    assert(s == "Exemplars");

    como::TlsfString const c("Exemplary");
    char const& back2 = c.back();
    assert(back2 == 'y');
}

void test_data() {
    como::TlsfString const s("Emplary");
    assert(s.size() == std::strlen(s.data()));
    assert(std::equal(s.begin(), s.end(), s.data()));
    assert(std::equal(s.data(), s.data() + s.size(), s.begin()));
    assert('\0' == *(s.data() + s.size()));
}

extern "C" void c_func(const char* c_str) {
    printf("以 '%s' 调用 c_func\n", c_str);
}

void test_c_str() {
    como::TlsfString const s("Emplary");
    const char* p = s.c_str();
    assert(s.size() == std::strlen(p));
    assert(std::equal(s.begin(), s.end(), p));
    assert(std::equal(p, p + s.size(), s.begin()));
    assert('\0' == *(p + s.size()));

    c_func(s.c_str());
}

void test_begin_cbegin() {
    como::TlsfString s("Exemplar");
    *s.begin() = 'e';
    assert(s == "exemplar");

    auto i = s.cbegin();
    assert(*i == 'e');
}

void test_end_cend() {
    como::TlsfString s("Exemparl");
    std::next_permutation(s.begin(), s.end());

    como::TlsfString c;
    std::copy(s.cbegin(), s.cend(), std::back_inserter(c));
    assert(c == "Exemplar");
}

void test_rbegin_crbegin() {
    como::TlsfString s("Exemplar!");
    *s.rbegin() = 'y';
    assert(s == "Exemplary");

    como::TlsfString c;
    std::copy(s.crbegin(), s.crend(), std::back_inserter(c));
    assert(c == "yralpmexE");
}

void test_rend_crend() {
    como::TlsfString p("[A man, a plan, a canal: Panama]");
    como::TlsfString q;

    std::copy(p.crbegin(), p.crend(), std::back_inserter(q));
    assert(q == "]amanaP :lanac a ,nalp a ,nam A[");

    std::copy(q.crbegin(), q.crend(), p.rbegin());
    assert(p == "]amanaP :lanac a ,nalp a ,nam A[");
}

void test_empty() {
    como::TlsfString s;
    assert(s.empty() == true);
    s = "Exemplar";
    assert(s.empty() == false);
    s = "";
    assert(s.empty() == true);
}

void test_size_length() {
    como::TlsfString s("Exemplar");
    assert(8 == s.size());
    assert(s.size() == s.length());
    assert(s.size() == static_cast<std::string::size_type>(
               std::distance(s.begin(), s.end())));
}

void test_max_size() {
    como::TlsfString s;
    printf("%ld", s.max_size());
}

void test_reserve_capacity_shrink_to_fit() {
    como::TlsfString s;
    assert(s.capacity() == 15);
    const size_t new_cap(101u);
    s.reserve(new_cap);
    assert ( s.capacity() >= new_cap);
    s.shrink_to_fit();
    assert(s.capacity() == 15);
}

void test_clear() {
    como::TlsfString s("Exemplar");
    size_t const capacity = s.capacity();
    s.clear();
    assert(s.empty());
    assert(s.size() == 0);
    assert(s.capacity() == capacity);
}

void test_insert() {
    como::TlsfString s = "xmplr";
    //insert(size_type index, size_type count, char ch)
    s.insert(0, 1, 'E');
    assert("Exmplr" == s);

    // insert(size_type index, const char* s)
    s.insert(2, "e");
    assert("Exemplr" == s);

    // insert(size_type index, string const& str)
    s.insert(6, como::TlsfString("a"));
    assert("Exemplar" == s);

    // insert(size_type index, string const& str,
    //        size_type s_index, size_type count)
    s.insert(8, como::TlsfString(" is an example string."), 0, 14);
    assert("Exemplar is an example" == s);

    // insert(const_iterator pos, char ch)
    s.insert(s.cbegin() + s.find_first_of('n') + 1, ':');
    assert("Exemplar is an: example" == s);

    // insert(const_iterator pos, size_type count, char ch)
    s.insert(s.cbegin() + s.find_first_of(':') + 1, 2, '=');
    assert("Exemplar is an:== example" == s);

    // insert(const_iterator pos, InputIt first, InputIt last)
    {
        std::string seq = " string";
        s.insert(s.begin() + s.find_last_of('e') + 1,
                 std::begin(seq), std::end(seq));
        assert("Exemplar is an:== example string" == s);
    }

    // insert(const_iterator pos, std::initializer_list<char>)
    s.insert(s.cbegin() + s.find_first_of('g') + 1, {'.'});
    assert("Exemplar is an:== example string." == s);
}

void test_erase() {
    como::TlsfString s = "This Is An Example";
    assert(s == "This Is An Example" );

    s.erase(7, 3); // 使用重载 (1) 擦除 " An"
    assert(s == "This Is Example");

    s.erase(std::find(s.begin(), s.end(), ' ')); // 使用重载 (2) 擦除第一个 ' '
    assert(s == "ThisIs Example");

    s.erase(s.find(' ')); // 使用重载 (1) 截掉从 ' ' 到字符串结尾的部分
    assert(s == "ThisIs");

    auto it = std::next(s.begin(), s.find('s')); // 获取指向第一个 's' 的迭代器
    s.erase(it, std::next(it, 2)); // 使用重载 (3) 擦除 "sI"
    assert(s == "This");
}

void test_push_back() {
    como::TlsfString str("Short string");
    assert(str.size() == 12);
    str.push_back('!');
    assert(str.size() == 13);
}

void test_pop_back() {
    como::TlsfString str("Short string!");
    assert(str.size() == 13);
    str.pop_back();
    assert(str.size() == 12);
}

void test_append() {
    como::TlsfString str = "std::string";
    const char* cptr = "C-string";
    const char carr[] = "Two and one";

    como::TlsfString result;

    // 1) 后附 char 3 次。
    // 注意：这是仅有的接受 “CharT” 的重载。
    result.append(3, '*');
    assert(result == "***");

    // 2) 后附定长 C 风格字符串
    result.append(cptr, 5);
    assert(result == "***C-str");

    // 3) 后附空终止 C 风格字符串
    // 注意：因为 “append” 返回 *this，所以我们能一同链式调用。
    result.append(1, ' ').append(cptr);
    assert(result == "***C-str C-string");

    // 6) 后附整个字符串
    result.append(1, ' ').append(str);
    assert(result == "***C-str C-string std::string");

    // 7) 后附字符串的一部分
    result.append(str, 3, 2);
    assert(result == "***C-str C-string std::string::");

    // 8) 后附范围
    result.append(&carr[5], &carr[6]);
    assert(result == "***C-str C-string std::string::n");

    // 9) 后附初始化器列表
    result.append({'p', 'o', 's'});
    assert(result == "***C-str C-string std::string::npos");
}

void test_operator_plus_assign() {
    como::TlsfString str;
    str.reserve(50);
    assert(str == "");
    str += "This";
    assert(str == "This");
    str += como::TlsfString(" is ");
    assert(str == "This is ");
    str += 'a';
    assert(str == "This is a");
    str += {' ', 's', 't', 'r', 'i', 'n', 'g', '.'};
    assert(str == "This is a string.");
}

void test_replace() {
    como::TlsfString str = "C++ primer 4ths";
    como::TlsfString replace = "is a books";
    como::TlsfString replace_str1 = str;
    replace_str1.replace(11, 4, "5th");
    assert(replace_str1 == "C++ primer 5th");

    como::TlsfString replace_str2 = str;
    replace_str2.replace(replace_str2.end() - 4, replace_str2.end(), "6th");
    assert(replace_str2 == "C++ primer 6th");

    como::TlsfString replace_str3 = str;
    replace_str3.replace(11, 4, replace, 0, replace.size() - 1);
    assert(replace_str3 == "C++ primer is a book");
}

void test_copy() {
    como::TlsfString foo("WINE");
    char bar[4] {};
    foo.copy(bar, sizeof(bar) - 1);
    assert(strcmp(bar, "WIN") == 0);
}

void test_resize() {
    const unsigned desired_length{8};
    como::TlsfString long_string("Where is the end?");
    como::TlsfString short_string("H");
    long_string.resize(desired_length);
    assert(long_string == "Where is");
    short_string.resize(desired_length, 'a');
    assert(short_string == "Haaaaaaa");
    como::TlsfString s;
    // bool flag1=false,flag2=false,flag3=false;
    // try
    // {
    //     // 大小 OK，没有 length_error（可能会抛出 bad_alloc）
    //     s.resize(s.max_size() - 1, 'x');
    // }
    // catch (const std::bad_alloc& ex)
    // {
    //     flag1 = true;
    // }

    // try
    // {
    //     // 大小 OK，没有 length_error（可能会抛出 bad_alloc）
    //     s.resize(s.max_size(), 'x');
    // }
    // catch (const std::bad_alloc& ex)
    // {
    //     flag2 = true;
    // }

    // try
    // {
    //     // 大小错误，抛出 length_error
    //     s.resize(s.max_size() + 1, 'x');
    // }
    // catch (const std::length_error& ex)
    // {
    //     flag3 = true;
    // }
    // assert(flag1&&flag2&&flag3);
}

void test_swap() {
    como::TlsfString a("AAA");
    como::TlsfString b("BBBB");
    a.swap(b);
    assert((a == "BBBB") && (b == "AAA"));
}

void test_find_substr() {
    size_t n;
    como::TlsfString s("This is a string");
    n = s.find("is");
    assert(n == 2 && s.substr(n) == "is is a string");
    n = s.find("is", 5);
    assert(n == 5 && s.substr(n) == "is a string");
    n = s.find('a');
    assert(n == 8 && s.substr(n) == "a string");
    n = s.find('q');
    assert(n == como::TlsfString::npos);
}

void test_compare() {
    como::TlsfString batman("Batman");
    como::TlsfString superman("Superman");
    int compare_result = 0;
    compare_result = batman.compare(superman);
    assert(compare_result < 0 );
    compare_result = batman.compare(3, 3, superman);
    assert(compare_result > 0);
    compare_result = batman.compare(3, 3, superman, 5, 3);
    assert(compare_result == 0);
    compare_result = batman.compare(3, 3, superman, 5);
    assert(compare_result == 0);
    compare_result = batman.compare("Superman");
    assert(compare_result < 0);
    compare_result = batman.compare(3, 3, "Superman");
    assert(compare_result > 0);
    compare_result = batman.compare(0, 3, "Superman", 5);
    assert(compare_result < 0);
}

void test_operator_plus() {
    como::TlsfString s1 = "Hello";
    como::TlsfString s2 = "World";
    const char* end = "!";
    assert(s1 + ' ' + s2 + end == "Hello World!");
}

void test_operator_compare() {
    como::TlsfString s1 = "AAA";
    como::TlsfString s2 = "AAA";
    como::TlsfString s3 = "BBB";
    assert(s1 == s2);
    assert(s1 <= s2);
    assert(s1 >= s2);
    assert(s1 < s3);
    assert(s3 > s1);
    assert(s1 != s3);
}

void test_std_swap() {
    como::TlsfString a = "AAA";
    como::TlsfString b = "BBBB";
    std::swap(a, b);
    assert(a == "BBBB" && b == "AAA");
}

int main() {
    test_constructor();
    test_at();
    test_operator_at();
    test_front();
    test_back();
    test_data();
    test_c_str();
    test_begin_cbegin();
    test_end_cend();
    test_rbegin_crbegin();
    test_rend_crend();
    test_empty();
    test_size_length();
    test_max_size();
    test_reserve_capacity_shrink_to_fit();
    test_clear();
    test_insert();
    test_erase();
    test_push_back();
    test_pop_back();
    test_append();
    test_operator_plus_assign();
    test_replace();
    test_copy();
    test_resize();
    test_swap();
    test_find_substr();
    test_compare();
    test_operator_plus();
    test_operator_compare();
    test_std_swap();
}
